在軟體開發的世界中,良好的溝通是成功的關鍵。想像一下,如果建築師沒有藍圖,要如何與工程團隊溝通建築設計?同樣地,軟體開發也需要一種標準化的「藍圖」來表達系統架構和設計想法。這就是 UML(Unified Modeling Language,統一塑模語言) 的價值所在。
UML 是一種標準化的視覺化建模語言,用於:
💡 UML 不是程式語言,而是一種圖形化的表達方式,讓複雜的系統設計變得容易理解。
UML 2.x 定義了 14 種不同類型的圖表,主要分為兩大類:
描述系統的靜態結構:
描述系統的動態行為:
類別圖是 UML 中最常用、最重要的圖表之一。它展示系統中的類別、屬性、方法,以及類別之間的關係。
┌─────────────────────┐
│    ClassName        │  ← 類別名稱
├─────────────────────┤
│ - attribute1: Type  │  ← 屬性(- 表示 private)
│ + attribute2: Type  │  ← 屬性(+ 表示 public)
│ # attribute3: Type  │  ← 屬性(# 表示 protected)
├─────────────────────┤
│ + method1(): void   │  ← 方法
│ - method2(): Type   │
│ # method3(): Type   │
└─────────────────────┘
| 符號 | 意義 | Java 對應 | 
|---|---|---|
| + | Public | public | 
| - | Private | private | 
| # | Protected | protected | 
| ~ | Package | (預設,無修飾符) | 
{abstract}
<<interface>> 標籤類別圖的精髓在於表達類別之間的關係,以下是六種主要關係:
符號:───▷ (實線空心三角箭頭)
意義:子類別繼承父類別,"is-a" 關係
範例:
    ┌─────────┐
    │  動物   │
    └─────────┘
         △
         │
    ┌────┴────┐
    │         │
┌───────┐ ┌───────┐
│  貓   │ │  狗   │
└───────┘ └───────┘
貓 是一種 動物,狗 是一種 動物。
符號:┄┄▷ (虛線空心三角箭頭)
意義:類別實作介面
範例:
    ┌─────────────┐
    │<<interface>>│
    │   可飛行    │
    └─────────────┘
         △
         ┊
    ┌────┴────┐
    ┊         ┊
┌───────┐ ┌───────┐
│  鳥   │ │ 飛機  │
└───────┘ └───────┘
符號:─── (實線)
意義:類別之間有某種關聯,"has-a" 或 "uses" 關係
範例:
┌─────┐       ┌─────┐
│教師 │──────>│學生 │
└─────┘ 1  *  └─────┘
一位教師可以教導多位學生。
多重性標記:
1 : 正好一個0..1 : 零或一個* 或 0..* : 零或多個1..* : 一或多個n..m : n 到 m 個符號:───◇ (實線空心菱形)
意義:整體包含部分的關係,但部分可以獨立存在(弱擁有關係)
範例:
┌─────┐       ┌─────┐
│部門 │◇──────│員工 │
└─────┘       └─────┘
部門包含員工,但員工離開部門後仍然存在。
符號:───◆ (實線實心菱形)
意義:整體包含部分的關係,部分不能獨立於整體存在(強擁有關係)
範例:
┌─────┐       ┌─────┐
│房子 │◆──────│房間 │
└─────┘       └─────┘
房子包含房間,房子不存在了,房間也就不存在了。
符號:┄┄> (虛線箭頭)
意義:一個類別使用另一個類別,通常是暫時性的關係
範例:
┌─────┐       ┌──────────┐
│訂單 │ ┄┄┄┄┄> │折扣計算器│
└─────┘       └──────────┘
訂單在計算價格時會使用折扣計算器。
設計一個 RPG 遊戲的角色系統:
┌─────────────────────────┐
│   <<abstract>>          │
│      Character          │
├─────────────────────────┤
│ - name: String          │
│ - level: int            │
│ - hp: int               │
├─────────────────────────┤
│ + Character(name)       │
│ + attack(): void        │ ← 抽象方法
│ + defend(): void        │
│ + levelUp(): void       │
└─────────────────────────┘
           △
           │ (繼承)
    ┌──────┴──────┐
    │             │
┌───────────┐ ┌───────────┐
│  Warrior  │ │   Mage    │
├───────────┤ ├───────────┤
│-sword:    │ │-staff:    │
│ String    │ │ String    │
├───────────┤ ├───────────┤
│+Warrior() │ │+Mage()    │
│+attack()  │ │+attack()  │
│+slash()   │ │+cast()    │
└───────────┘ └───────────┘
// 抽象父類別
public abstract class Character {
    private String name;
    private int level;
    private int hp;
    
    public Character(String name) {
        this.name = name;
        this.level = 1;
        this.hp = 100;
    }
    
    // 抽象方法,子類別必須實作
    public abstract void attack();
    
    // 具體方法
    public void defend() {
        System.out.println(name + " 防禦中...");
    }
    
    public void levelUp() {
        level++;
        hp += 50;
        System.out.println(name + " 升級到 Lv." + level + "!生命值: " + hp);
    }
    
    // Getters
    public String getName() {
        return name;
    }
    
    public int getLevel() {
        return level;
    }
    
    public int getHp() {
        return hp;
    }
}
// 戰士子類別
public class Warrior extends Character {
    private String sword;
    
    public Warrior(String name) {
        super(name);
        this.sword = "鐵劍";
    }
    
    @Override
    public void attack() {
        System.out.println(getName() + " 戰士揮劍斬擊!");
    }
    
    // 戰士專屬技能
    public void slash() {
        System.out.println(getName() + " 使用 " + sword + " 進行強力斬擊!造成 150% 傷害!");
    }
    
    // 升級時可以強化武器
    public void upgradeSword() {
        if (getLevel() >= 5) {
            this.sword = "鋼劍";
            System.out.println("武器升級為:" + sword);
        } else if (getLevel() >= 10) {
            this.sword = "神劍";
            System.out.println("武器升級為:" + sword);
        }
    }
}
// 法師子類別
public class Mage extends Character {
    private String staff;
    private int mana;
    
    public Mage(String name) {
        super(name);
        this.staff = "木杖";
        this.mana = 100;
    }
    
    @Override
    public void attack() {
        System.out.println(getName() + " 法師施放魔法攻擊!");
        useMana(10);
    }
    
    // 法師專屬技能
    public void cast() {
        if (mana >= 30) {
            System.out.println(getName() + " 使用 " + staff + " 施放火球術!造成 200% 傷害!");
            useMana(30);
        } else {
            System.out.println("魔力不足!");
        }
    }
    
    private void useMana(int amount) {
        mana -= amount;
        System.out.println("剩餘魔力: " + mana);
    }
    
    public void restoreMana() {
        mana = 100;
        System.out.println("魔力已恢復!");
    }
}
public class RPGGameExample {
    public static void main(String[] args) {
        // 創建戰士角色
        Warrior warrior = new Warrior("亞瑟");
        System.out.println("=== 戰士 " + warrior.getName() + " ===");
        warrior.attack();      // 一般攻擊
        warrior.slash();       // 專屬技能
        warrior.defend();      // 防禦
        warrior.levelUp();     // 升級
        
        System.out.println();
        
        // 創建法師角色
        Mage mage = new Mage("梅林");
        System.out.println("=== 法師 " + mage.getName() + " ===");
        mage.attack();         // 一般攻擊
        mage.cast();           // 專屬技能
        mage.attack();         // 再次攻擊
        mage.attack();         // 魔力不足
        mage.restoreMana();    // 恢復魔力
        mage.levelUp();        // 升級
    }
}
=== 戰士 亞瑟 ===
亞瑟 戰士揮劍斬擊!
亞瑟 使用 鐵劍 進行強力斬擊!造成 150% 傷害!
亞瑟 防禦中...
亞瑟 升級到 Lv.2!生命值: 150
=== 法師 梅林 ===
梅林 法師施放魔法攻擊!
剩餘魔力: 90
梅林 使用 木杖 施放火球術!造成 200% 傷害!
剩餘魔力: 60
梅林 法師施放魔法攻擊!
剩餘魔力: 50
梅林 法師施放魔法攻擊!
剩餘魔力: 40
魔力已恢復!
梅林 升級到 Lv.2!生命值: 150
每個類別應該只有一個職責。
❌ 不好的設計:
public class User {
    private String name;
    
    public void saveToDatabase() { }     // 資料庫操作
    public void sendEmail() { }          // 郵件發送
    public void generateReport() { }     // 報告生成
}
✅ 好的設計:
public class User {
    private String name;
    // 只負責使用者資料
}
public class UserRepository {
    public void save(User user) { }  // 負責資料庫操作
}
public class EmailService {
    public void send(User user) { }  // 負責郵件發送
}
繼承建立了強耦合關係,組合更加靈活。
❌ 過度使用繼承:
    Animal
      │
  ┌───┴───┐
  │       │
 Bird    Fish
  │       │
┌─┴─┐     │
│   │     │
Duck Swan Shark
✅ 使用組合:
┌─────────┐      ┌──────────────┐
│ Animal  │◇────>│ MoveBehavior │
└─────────┘      └──────────────┘
                       △
                  ┌────┴────┐
            ┌──────┐    ┌──────┐
            │ Fly  │    │ Swim │
            └──────┘    └──────┘
不要強迫類別實作它們不需要的方法。
❌ 過大的介面:
public interface Worker {
    void work();
    void eat();
    void sleep();
    void fly();  // 不是所有 worker 都會飛
}
✅ 分離介面:
public interface Workable {
    void work();
}
public interface Eatable {
    void eat();
}
public interface Flyable {
    void fly();
}
UML 的定義與用途
UML 圖表分類
類別圖的完整知識